package com.yyfax.pay.third.facade.minsheng.util;

import com.yyfax.commons.lang.NumberUtil;
import com.yyfax.pay.third.facade.lianlian.LianlianApi;
import com.yyfax.pay.third.model.to.yyfax.PayoutBatchOrderYYParams;
import com.yyfax.pay.third.model.to.yyfax.PayoutBatchOrderYYParams.PayoutOrderYY;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * @author AlanQiu on 2017/4/18.
 */
public class MinshengUtil {

	private static Logger logger = LoggerFactory.getLogger(LianlianApi.class);

	private static final String ENCODING = "UTF-8";

	public static byte h2c(byte h) {
		if (h >= '0' && h <= '9')
			return (byte) (h - '0');
		if (h >= 'a' && h <= 'f')
			return (byte) (h - 'a' + 10);
		if (h >= 'A' && h <= 'F')
			return (byte) (h - 'A' + 10);

		return -1;
	}

	public static byte[] hex2bin(String hex) {
		byte[] b = new byte[hex.length() / 2];
		byte[] hb = hex.getBytes();

		byte h, l;
		int n = 0;

		for (int i = 0; i < b.length; i++) {
			h = h2c(hb[i * 2]);
			l = h2c(hb[i * 2 + 1]);
			b[n++] = (byte) (h << 4 | l);
		}

		return b;
	}

	public static String encryptFile(String fileName, String keys, String ivs, String ALGORITHM) throws Exception {
		byte[] keyBytes = hex2bin(keys);
		byte[] IVBytes = hex2bin(ivs);

		Key key = new SecretKeySpec(keyBytes, "DESede");
		IvParameterSpec ivParameterSpec = new IvParameterSpec(IVBytes);

		Cipher cipher = Cipher.getInstance(ALGORITHM);
		cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);

		File file = new File(fileName);
		InputStream inputStream = new FileInputStream(fileName);
		byte[] fileData = new byte[(int) file.length()];
		int readLength = inputStream.read(fileData, 0, fileData.length);

		/*
		 * logger.info("\n========= UNENCTYPT DATA =========");
		 * for (int i = 0; i < fileData.length; i++) {
		 * if (i % 16 == 0)
		 * System.out.println();
		 * logger.info(String.format("%02X ", fileData[i]));
		 * }
		 */

		byte[] encryptData = cipher.doFinal(fileData);

		/*
		 * System.out.print("\n========== ENCTYPT DATA ==========");
		 * for (int i = 0; i < encryptData.length; i++) {
		 * if (i % 16 == 0)
		 * System.out.println();
		 * System.out.format("%02X ", encryptData[i]);
		 * }
		 */

		// logger.info("\nlength: " + encryptData.length);
		String encFileName = fileName + ".enc";
		inputStream.close();

		OutputStream outputStream = new FileOutputStream(encFileName);
		outputStream.write(encryptData, 0, encryptData.length);
		outputStream.close();

		return encFileName;
	}

	public static String decryptFile(String fileName, String keys, String ivs, String ALGORITHM) throws Exception {

		Cipher cipher = Cipher.getInstance(ALGORITHM);

		byte[] keyBytes = hex2bin(keys);
		byte[] IVBytes = hex2bin(ivs);

		Key key = new SecretKeySpec(keyBytes, "DESede");
		IvParameterSpec ivParameterSpec = new IvParameterSpec(IVBytes);

		cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);

		File file = new File(fileName);
		InputStream inputStream = new FileInputStream(fileName);
		byte[] fileData = new byte[(int) file.length()];
		int readLength = inputStream.read(fileData, 0, fileData.length);

		/*
		 * logger.info("\n========= UNDECRYPT DATA =========");
		 * for (int i = 0; i < fileData.length; i++) {
		 * if (i % 16 == 0)
		 * System.out.println();
		 * logger.info(String.format("%02X ", fileData[i]));
		 * }
		 */

		byte[] decryptData = cipher.doFinal(fileData);
		String decFileName = fileName + ".dec";
		inputStream.close();

		OutputStream outputStream = new FileOutputStream(decFileName);
		outputStream.write(decryptData, 0, decryptData.length);
		outputStream.close();

		/*
		 * logger.info("\n========== DECRYPT DATA ==========");
		 * for (int i = 0; i < decryptData.length; i++) {
		 * if (i % 16 == 0)
		 * System.out.println();
		 * logger.info(String.format("%02X ", decryptData[i]));
		 * }
		 */

		return decFileName;
	}

	public static byte[] encrypt(byte[] src, String keys, String ivs, String ALGORITHM) throws Exception {

		Cipher cipher = Cipher.getInstance(ALGORITHM);

		byte[] keyBytes = hex2bin(keys);
		byte[] IVBytes = hex2bin(ivs);

		Key key = new SecretKeySpec(keyBytes, "DESede");
		IvParameterSpec ivParameterSpec = new IvParameterSpec(IVBytes);

		cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);

		byte[] k = key.getEncoded();
		/*
		 * for (int i = 0; i < k.length; i++)
		 * logger.info(String.format("%02X ", k[i]));
		 */

		k = ivParameterSpec.getIV();
		/*
		 * for (int i = 0; i < k.length; i++)
		 * logger.info(String.format("%02X ", k[i]));
		 */

		byte[] d = cipher.doFinal(src);
		return d;
	}

	public static byte[] decrypt(byte[] src, String keys, String ivs, String ALGORITHM) throws Exception {

		Cipher cipher = Cipher.getInstance(ALGORITHM);

		byte[] keyBytes = hex2bin(keys);
		byte[] IVBytes = hex2bin(ivs);

		Key key = new SecretKeySpec(keyBytes, "DESede");
		IvParameterSpec ivParameterSpec = new IvParameterSpec(IVBytes);
		cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);

		byte[] k = key.getEncoded();
		/*
		 * logger.info("\n=============== key =============");
		 * for (int i = 0; i < k.length; i++)
		 * logger.info(String.format("%02X ", k[i]));
		 */

		k = ivParameterSpec.getIV();
		/*
		 * for (int i = 0; i < k.length; i++)
		 * logger.info(String.format("%02X ", k[i]));
		 */

		byte[] d = cipher.doFinal(src);
		return d;
	}

	public static String getMD5(byte[] data) throws NoSuchAlgorithmException {
		MessageDigest md = MessageDigest.getInstance("MD5");
		md.update(data);
		byte[] md5 = md.digest();
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < md5.length; i++) {
			sb.append(String.format("%02X", md5[i]));
		}

		return sb.toString();
	}

	public static String getFileMD5(String fileName, byte[] fr, byte[] end) throws IOException, NoSuchAlgorithmException {
		if (fr == null) {
			return null;
		}
		File file = new File(fileName);
		int len = (int) file.length();
		int dataLength = len;
		int tmpL = 0;

		if (fr != null)
			dataLength += fr.length;

		if (end != null)
			dataLength += end.length;

		byte[] fileData = new byte[dataLength];

		if (fr != null) {
			System.arraycopy(fr, 0, fileData, 0, fr.length);
			tmpL += fr.length;
		}

		InputStream inputStream = new FileInputStream(file);
		tmpL += inputStream.read(fileData, fr.length, len);

		/*
		 * for (int i = 0; i < fileData.length; i++) {
		 * if (i % 16 == 0)
		 * logger.info(String.format("%02X ", fileData[i]));
		 * }
		 */

		if (end != null)
			System.arraycopy(end, 0, fileData, tmpL, end.length);

		return getMD5(fileData);
	}

	public static String gzipFile(String fileName) {
		FileInputStream fileInputStream = null;
		GZIPOutputStream gzipOutputStream = null;
		FileOutputStream fileOutputStream = null;
		String zipName = fileName + ".gz";

		byte[] buffer = new byte[1024];
		int read_length = 0;

		try {
			fileInputStream = new FileInputStream(fileName);
			fileOutputStream = new FileOutputStream(zipName);
			gzipOutputStream = new GZIPOutputStream(fileOutputStream);
			while ((read_length = fileInputStream.read(buffer, 0, 1024)) != -1) {
				gzipOutputStream.write(buffer, 0, read_length);
			}

			return zipName;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				fileInputStream.close();
				gzipOutputStream.close();
				fileOutputStream.close();
			} catch (Exception e) {
			}
		}

		return null;
	}

	public static String gunzipFile(String fileName) {
		FileInputStream fileInputStream = null;
		GZIPInputStream gzipInputStream = null;
		FileOutputStream fileOutputStream = null;
		String unzipName = fileName + ".gunz";

		byte[] buffer = new byte[1024];
		int read_length = 0;

		try {
			fileInputStream = new FileInputStream(fileName);
			fileOutputStream = new FileOutputStream(unzipName);
			gzipInputStream = new GZIPInputStream(fileInputStream);
			while ((read_length = gzipInputStream.read(buffer, 0, 1024)) != -1) {
				fileOutputStream.write(buffer, 0, read_length);
			}

			return unzipName;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				fileOutputStream.close();
				gzipInputStream.close();
				fileInputStream.close();
			} catch (Exception e) {
			}
		}

		return null;
	}

	public static Map<String, String> genFile(String code, String date, int batNo, String sourceName, String name, String path,
			List<PayoutOrderYY> lists, String remark) throws Exception {
		Map<String, String> params = new HashMap<String, String>();

		String dataFile = path + sourceName;

		String buffer = null;

		FileOutputStream output = new FileOutputStream(dataFile);
		OutputStreamWriter outWriter = new OutputStreamWriter(output, ENCODING);
		ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();

		double totAmount = 0.0;

		int index = 0;
		try {
			for (PayoutOrderYY order : lists) {
				String payRemark = remark + order.getPayeeName();
				totAmount = totAmount + NumberUtil.fen2yuan(order.getAmount());

				index++;
				buffer = String.format("%d|%s|%s|%.2f|%s|", index, order.getPayeeCardNo(), order.getPayeeName(),
						NumberUtil.fen2yuan(order.getAmount()), payRemark);

				String src = String.format("%s%.15s%.8s%03d%05d", buffer, code, date, batNo, index);
				logger.info(src);

				String md5 = MinshengUtil.getMD5(src.getBytes(ENCODING));

				byteOutput.write(buffer.getBytes());
				byteOutput.write((md5 + "\n").getBytes());

				// 生成流水号，规则：日期+批次号+明细索引号
				String serialNo = String.format("%s%03d%05d", date, batNo, index);
				// order.setSerialno(serialNo);
			}

			String header = String.format("%s|%s|%d|%d|%.2f|%s|%s|\n", date, code, batNo, index, totAmount, name, "付款摘要");
			outWriter.write(header);
			outWriter.write(byteOutput.toString());
			outWriter.flush();
		} catch (Throwable e) {
			logger.error("生成批量文件时出错");
			throw e;
		} finally {
			outWriter.close();
			output.close();
			byteOutput.close();
		}

		params.put("dataFile", dataFile);
		params.put("totAmount", String.valueOf(totAmount));
		params.put("totCount", String.valueOf(index));
		return params;
	}

}
